﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Windows;
using SmartPaginator.Extensions;

namespace SmartPaginator.Elements
{
    public class CellElement : ItemsControl, IBlockElement, IBlockContainer
    {
        #region Constructor

        protected CellElement(TableCell source)
        {
            Source = source;
            this.Background = source.Background;
            this.MinHeight = source.GetLineHeight();
        }

        public CellElement(TableCell source, IElementFactory factory): this(source)
        {
            if (factory == null)
                throw new ArgumentNullException("factory");

            Grid.SetColumnSpan(this, source.ColumnSpan);
            Grid.SetRowSpan(this, source.RowSpan);

            foreach (object block in source.Blocks)
            {
                var element = factory.GetElementForContent(block);
                this.Items.Add(element);
            }
        }

        #endregion

        #region Properties

        public TableCell Source { get; private set; }

        public IEnumerable<IBlockElement> Blocks 
        {
            get
            {
                return Items.Cast<IBlockElement>();
            }
        }

        #endregion

        #region IBlockElement members

        public virtual void AddChildElement(UIElement child)
        {
            this.Items.Add(child);
        }

        public virtual IBlockElement GetEmptyCopy()
        {
            var result = new CellElement(this.Source);
            Grid.SetColumn(result, Grid.GetColumn(this));
            Grid.SetColumnSpan(result, Grid.GetColumnSpan(this));
            result.Background = this.Background;
            return result;
        }

        public IBlockElement GetPreviousBlock(IBlockElement block)
        {
            int index = Items.IndexOf(block as UIElement);
            if (index < 0)
                throw new ArgumentException("The provided block is not a child of this CellElement.");

            if (index == 0)
                return null;

            return Items[index - 1] as IBlockElement;
        }


        public virtual HarvestResult Harvest(HarvestingContext context, double remainingHeight, IBlockElement previousElement)
        {
            var harvestedElement = this.GetEmptyCopy() as CellElement;

            if (this.Items.Count == 0)
            {
                // If it has no content, mark it as fully harvested
                context.MarkAsFullyHarvested(this);
            }
            else
            {
                // Harvest the content
                IBlockElement previousChild = null;
                foreach (UIElement child in this.Items)
                {
                    if (context.IsFullyHarvested(child))
                        continue;

                    var childBlock = child as IBlockElement;
                    var childResult = childBlock.Harvest(context, remainingHeight, previousChild);
                    remainingHeight = childResult.RemainingHeight;

                    // Let's see if child fit
                    if (childResult.HarvestedElement == null)
                    {
                        break;
                    }

                    // Add child to harvested element
                    harvestedElement.AddChildElement(childResult.HarvestedElement);

                    previousChild = childBlock;
                    if (this.Items.IndexOf(child) == Items.Count - 1)
                    {
                        // Last child. We are finished, if it is finished
                        if (context.IsFullyHarvested(child))
                            context.MarkAsFullyHarvested(this);
                    }
                }
            }

            return new HarvestResult(harvestedElement, remainingHeight);
        }

        public virtual double CalculateBottomMargin()
        {
            return 0;
        }

        public virtual double CalculateTopMargin()
        {
            return 0;
        }

        #endregion

        #region CalculateHeight Methods

        public double CalculateHeight()
        {
            double result = 0;

            foreach (var child in Items)
            {
                var element = child as IBlockElement;
                if (element != null)
                {
                    result += element.CalculateHeight();
                }
            }

            return result;
        }

        public virtual double CalculateMinHeight()
        {
            if (Items.Count > 0)
            {
                //if (this.KeepTogether)
                //{
                //    return CalculateHeight();
                //}
                //else
                {
                    var firstBlock = Items[0] as IBlockElement;
                    if (firstBlock != null)
                        return firstBlock.CalculateMinHeight();
                }
            }

            return Source.GetLineHeight();
        }

        #endregion

        #region Measure and Arrange Overrides

        protected override Size MeasureOverride(Size constraint)
        {
            var result = base.MeasureOverride(constraint);
            return result;
        }

        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            var result = base.ArrangeOverride(arrangeBounds);
            return result;
        }

        #endregion

    }
}
